﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Terminal.Advanced_AEUC_settings_controls.Settings_AUX;

namespace Terminal.Advanced_AEUC_settings_controls.Scripting
{
    /// <summary>
    /// Wait for message script action
    /// </summary>
    class ScrActWaitMsg : ScrBasicAction
    {
        /// <summary>
        /// Semaphore for message reception synchronization
        /// </summary>
        AutoResetEvent waitMsg;
        /// <summary>
        /// Byte array representing the message received.
        /// </summary>
        byte[] msgBytestream;
        /// <summary>
        /// Reference to the messaging script module
        /// </summary>
        ScrMsgCntr msgCntr;

        /// <summary>
        /// Public constructor for the script action
        /// </summary>
        /// <param name="iDispName">Action display text</param>
        /// <param name="iUsageHelp">Usage help and comments text for the action</param>
        /// <param name="iMsgCntr">Script message dispatch object</param>
        public ScrActWaitMsg(string iDispName, string iUsageHelp, ScrMsgCntr iMsgCntr)
            : base(iDispName, iUsageHelp)
        {
            waitMsg = new AutoResetEvent(false);
            msgCntr = iMsgCntr;
            msgCntr.DispatchEnabled = false;
            msgCntr.OnMessageReceived += ReceiveMsg;

            usageHelp += "Input params: GenMSG(message name) + GenPayloadStruct(payload struct name) + \n(optional)integer(timeout in milliseconds - if not set or zero, timeout is indefinite)\n";
        }

        /// <summary>
        /// Inform the wait for message action that a message has been received and provide the message data.
        /// </summary>
        /// <param name="byteStream">Byte array of the message data</param>
        public void ReceiveMsg(byte[] byteStream)
        {
            msgBytestream = byteStream;
            waitMsg.Set();
        }

        /// <summary>
        /// Perform the action using specified parameters.
        /// </summary>
        /// <param name="paramList">List of the input/output parameters for the action</param>
        /// <returns>True if action is succesful</returns>
        public override bool Do(List<object> paramList)
        {
            if (breakPoint)
                WaitUserInput();

            if (paramList.Count > 2 && paramList[0].GetType() == typeof(ScrRunTimeEnv) && paramList[1].GetType() == typeof(GenMSG) && paramList[2].GetType() == typeof(GenPayloadStruct))
            {
                int semDelay = -1;

                ScrRunTimeEnv rte = (ScrRunTimeEnv)(paramList[0]);
                GenMSG msgHdr = new GenMSG();
                msgHdr.msgId = new GenMSGId();
                msgHdr.msgStatus = new GenMSGStatus();
                GenMSG msgSend = (GenMSG)paramList[1];
                msgHdr.payloadStruct = (GenPayloadStruct)paramList[2];
                msgSend.payloadStruct = (GenPayloadStruct)paramList[2];

                if (paramList.Count > 3)
                    semDelay = (int)paramList[3];

                msgBytestream = null;

                DateTime startTime = DateTime.Now;

                do
                {
                    msgCntr.DispatchEnabled = true;
                    waitMsg.WaitOne(semDelay);

                    TimeSpan timeTaken = DateTime.Now - startTime;

                    if (msgBytestream != null)
                    {
                        msgHdr.msgBytestream = msgBytestream;
                        msgHdr.ParseMessageDataHeaderOnly();

                        if (msgHdr.msgId.UID == msgSend.msgId.UID)
                        {
                            msgHdr.ParseMessageData();
                            //ScrCmdParser.paramsStructureSettingsParse(msgHdr.payloadStruct, (GenPayloadStruct)paramList[2]);

                            rte.rteResult = (object)msgHdr.msgStatus.UID;

                            if (msgHdr.msgStatus.UID == 0 || !rte.StopOnErrorMsgStatus)
                                return true;
                            else
                                return false;
                        }
                    }

                    if (semDelay > -1 && timeTaken.Milliseconds > semDelay)
                        return false;
                } while (true);
            }

            return false;
        }

        /// <summary>
        /// Generate C source code for specified parameters
        /// </summary>
        /// <param name="paramlist">List of input/output parameters for the action</param>
        /// <returns>C source string</returns>
        public override string GenerateSourceCode(List<object> paramList)
        {
            string c_source = "Can't generate source - check script file!";

            if (paramList.Count > 1 && paramList[0].GetType() == typeof(ScrRunTimeEnv) && paramList[1].GetType() == typeof(GenMSG) && paramList[2].GetType() == typeof(GenPayloadStruct))
            {
                int semDelay = -1;
                GenMSG msgSend = (GenMSG)paramList[1];
                msgSend.payloadStruct = (GenPayloadStruct)paramList[2];

                if (paramList.Count > 3)
                    semDelay = (int)paramList[3];
                c_source = "";
                c_source += "\t{\n";
                c_source += "\t\tsystemMessage_t* tmpMsg = (systemMessage_t*)TIMM_OSAL_MallocExtn(sizeof(systemMessage_t), TIMM_OSAL_TRUE, 32, 0, NULL);\n";
                c_source += "\n";
                c_source += "\t\tdo\n";
                c_source += "\t\t{\n";
                c_source += "\t\t\treadMsg(tmpMsg, sizeof(systemMessage_t*));\n";
                c_source += "\t\t\tSCREXEC_DBG_PRINT(\"Received message %08X\", &tmpMsg);\n";
                c_source += "\t\t\tif (tmpMsg != NULL)\n";
                c_source += "\t\t\t{\n";
                c_source += "\t\t\t\tSCREXEC_DBG_PRINT(\"Received message id %d while waiting for id %d!\", tmpMsg->uIdChannel.nId, " + msgSend.msgDisplayName + "->uIdChannel.nId);\n";
                c_source += "\t\t\t\tif (tmpMsg->uIdChannel.nId == " + msgSend.msgDisplayName + "->uIdChannel.nId)\n";
                c_source += "\t\t\t\t{\n";
                c_source += "\t\t\t\t\tTIMM_OSAL_Memcpy(&" + msgSend.payloadStruct.dispName + ", tmpMsg->pPayload, sizeof(" + msgSend.payloadStruct.dispName + "));\n";
                c_source += "\t\t\t\t\t" + msgSend.msgDisplayName + "->nStatus = tmpMsg->nStatus;\n";
                c_source += "\t\t\t\t\tif (tmpMsg->nStatus != 0)\n";
                c_source += "\t\t\t\t\t{\n";
                c_source += "\t\t\t\t\t\tSCREXEC_DBG_PRINT(\"Received message id %d with status %d!\", tmpMsg->uIdChannel.nId, tmpMsg->nStatus);\n";
                c_source += "\t\t\t\t\t\twhile(1);\n";
                c_source += "\t\t\t\t\t}\n";
                c_source += "\t\t\t\t\tbreak;\n";
                c_source += "\t\t\t\t}\n";
                c_source += "\t\t\t}\n";
                c_source += "\t\t} while (1);\n";
                c_source += "\n";
                c_source += "\t\tSCREXEC_DBG_PRINT(\"Exiting message receive block.\", NULL);\n";
                c_source += "\t\tif (tmpMsg->pPayload != NULL)\n";
                c_source += "\t\t{\n";
                c_source += "\t\t\tTIMM_OSAL_Free(tmpMsg->pPayload);\n";
                c_source += "\t\t}\n";
                c_source += "\t\tTIMM_OSAL_Free(tmpMsg);\n";
                c_source += "\t}\n";
            }

            return c_source;
        }
        /// <summary>
        /// Returns a List containing objects of the parameter list required for the Do function.
        /// </summary>
        /// <returns>List of parameter objects</returns>
        public override List<Type> GetParamList()
        {
            List<Type> paramList = new List<Type>();
            paramList.Add(typeof(ScrRunTimeEnv));
            paramList.Add(typeof(GenMSG));
            paramList.Add(typeof(GenPayloadStruct));
            paramList.Add(typeof(int));

            return paramList;
        }

        /// <summary>
        /// Returns a List containing objects of the parameter list required for the GenerateSourceCode function.
        /// </summary>
        /// <returns>List of parameter objects</returns>
        public override List<Type> GetParamListSourceGen()
        {
            List<Type> paramList = new List<Type>();
            paramList.Add(typeof(ScrRunTimeEnv));
            paramList.Add(typeof(GenMSG));
            paramList.Add(typeof(GenPayloadStruct));
            paramList.Add(typeof(int));

            return paramList;
        }
    }
}
